# coding: utf8
"""
Doc:时间日期相关工具函数

Created on 2015年2月10日
"""
import os
import calendar
import math
import time
import datetime
import tzlocal
from datetime import timedelta, date
from pytz import timezone


class TimeZone:
    """
    时区
    """
    Tokyo = 'Asia/Tokyo'
    Seoul = 'Asia/Seoul'
    Shanghai = 'Asia/Shanghai'
    Hongkong = 'Hongkong'
    UTC = 'UTC'


def getCT(needMs=None, tz=None):
    """获取当前时间current time
    @:param needMs bool 如果为True,则保留毫秒,否则毫秒置为0
    @:param tz 时区名称
    """
    ct = datetime.datetime.now(timezone(tz)) if tz else datetime.datetime.now()
    if needMs is None:
        ct = ct.replace(microsecond=0)
    return ct


def getCD():
    """获取当前日期"""
    return date.today()


def isPassDate(dt):
    """判断某个日期是否在今天之前"""
    cd = getCD()
    if strToDate(dt) < cd:
        return True
    return False


def strToDate(strDate, split='-'):
    """字符串转换为date"""
    if isinstance(strDate, date):
        return strDate

    d = time.strptime(strDate, "%Y" + split + "%m" + split + "%d")
    return date(d.tm_year, d.tm_mon, d.tm_mday)


def isPassDay(dt):
    '''判断某个日期是否在今天之前'''
    ct = getCT()
    if strToDatetime(dt) < ct.replace(hour=0, minute=0, second=0):
        return True
    return False


def isPassOneDay(dt, ct):
    """判断某个日期是否在某天之前"""
    if strToDatetime(dt) < ct.replace(hour=0, minute=0, second=0):
        return True
    return False


def isSameWeek(dt):
    """
    判断某个日期是否和今天是同一周
    :param dt: datetime实例，目标时间
    :return:
    """
    dt_year, dt_week, dt_day = strToDatetime(dt).isocalendar()
    ct_year, ct_week, ct_day = getCT().isocalendar()
    if (dt_year, dt_week) == (ct_year, ct_week):
        return True
    return False


def has_pass_time(dt, ct=None, days=0, hours=0, minutes=0, seconds=0):
    """
    判断某个日期时间是距离现在是否已经度过了指定时间
    :param dt:datetime实例，目标时间
    :param ct:datetime实例，指定时间
    :param days:天数
    :param hours:小时数
    :param minutes:分钟数
    :param seconds:秒数
    :return:bool
    """
    if not ct:
        ct = getCT()
    dt = strToDatetime(dt)
    if dt < ct - datetime.timedelta(days=days, hours=hours, minutes=minutes,
                                    seconds=seconds):
        return True
    return False


def has_pass_sometime(dt, some_time, ct=None):
    """
    判断某个时间是否已经过某个时刻

    如果当前是10点，设置过期时间点为16点，那么它判断的是昨天的16点
    当当前是18点时，判断为今天的16点。

    :param datetime dt: 时间
    :param str some_time: 某个时刻, 格式 '00:00:00'
    :return: 是否过了某个时间
    :rtype: bool
    """
    # 当前时间
    if not ct:
        ct = getCT()

    hour, minute, second = map(int, some_time.split(':'))
    # 自定义0点
    zero_dt = ct.replace(hour=hour, minute=minute, second=second)

    # 如果当前时间小于自定义0点，则用过去一天的自定义0点
    if ct < zero_dt:
        zero_dt = zero_dt + datetime.timedelta(days=-1)

    # 对比
    if dt < zero_dt:
        return True
    return False


def checkStrIsDatetime(strTime):
    """检查某个字符串是否是时间"""
    if isinstance(strTime, datetime.datetime):
        return True

    if not isinstance(strTime, basestring):
        return False

    try:
        strTime = strTime.replace('/', '-')
        time.strptime(strTime, "%Y-%m-%d %H:%M:%S")
        return True
    except:
        return False


def checkStrIsDate(strDate):
    """检查某个字符串是否是日期"""
    if isinstance(strDate, datetime.date):
        return True

    if not isinstance(strDate, basestring):
        return False

    try:
        strDate = strDate.replace('/', '-')
        time.strptime(strDate, "%Y-%m-%d")
        return True
    except:
        return False


def checkStrIsTime(strTime):
    """检查某个字符串是否是时钟时间"""
    if isinstance(strTime, datetime.datetime):
        return True

    if not isinstance(strTime, basestring):
        return False

    try:
        strTime = strTime.replace('/', '-')
        time.strptime(strTime, "%H:%M:%S")
        return True
    except:
        return False


def strToTime(strTime, needMs=False):
    '''字符串转换为datetime  strTime为时钟时间'''
    if not isinstance(strTime, basestring):
        return None
    if needMs:
        return datetime.datetime.strptime(strTime, "%H:%M:%S.%f")
    return datetime.datetime.strptime(strTime, "%H:%M:%S")


def strToDatetime(strTime, split='-', needMs=False):
    '''字符串转换为datetime'''
    if isinstance(strTime, datetime.datetime):
        return strTime
    if needMs:
        return datetime.datetime.strptime(strTime, "%Y" + split + "%m" + split + "%d %H:%M:%S.%f")
    return datetime.datetime.strptime(strTime, "%Y" + split + "%m" + split + "%d %H:%M:%S")


def second_to_str(total_second):
    """给的总秒数  返回对应格式字符串 格式：**h**m**s"""
    total_second = int(total_second)
    seconds = total_second % 60
    total_second /= 60
    minute = total_second % 60
    total_second /= 60
    hours = total_second
    return str(hours) + 'h' + str(minute) + 'm' + str(seconds) + 's'


def get_another_time(base_time, **kwargs):
    """
    给的一个时间，以及对应参数，返回对应时间
    :param base_time: 基础时间
    :param kwargs: weeks=, days=, hours=, minutes=, seconds= 负数表示往前
    :return:
    """
    return base_time + datetime.timedelta(**kwargs)


def is_in_one_day(check_time, base_time=None):
    """
    判断check_time是否和base_time在同一天内
    :param check_time: 待检测的时间
    :param base_time: 基准时间 默认为当前时间
    :return:
    """
    if not base_time:
        base_time = getCT()
    start_time = base_time.replace(hour=0, minute=0, second=0)
    end_time = base_time.replace(hour=23, minute=59, second=59)
    if start_time <= check_time <= end_time:
        return True
    return False


def getCountdown(dt1, dt2, is_calc_ms=False):
    '''计算时间差
    @param dt1,dt2: datetime
    @return: 两个日期的时间差
    '''
    if is_calc_ms:
        return (dt1-dt2).total_seconds()
    return int((dt1 - dt2).total_seconds())


def getDaysCountdown(dt1, dt2):
    '''
    计算天数差
    :param dt1:
    :param dt2:
    :return:
    '''
    dt1 = dt1.replace(hour=0, minute=0, second=0, microsecond=0)
    dt2 = dt2.replace(hour=0, minute=0, second=0, microsecond=0)
    return (dt1 - dt2).days


def addSeconds(dt, second):
    '''增加时间
    @param dt: datetime
    @param seconds: int 增加的秒数
    @return datetime 增加后的时间
    '''
    return dt + timedelta(seconds=second)


def addTimes(dt, seconds=0, days=0, hours=0, minutes=0):
    """
    增加时间
    :param dt: datetime
    :param days: int 增加的天数
    :param hours: int 增加的小时
    :param minutes: int 增加的分钟数
    :param seconds: int 增加的秒数
    :return: datetime 增加后的时间
    """
    return dt + timedelta(days=days, seconds=seconds, hours=hours, minutes=minutes)


def discountTime(needTime, buffLevel=0, num=1, vipBuff=0, degree=5):
    '''计算needTime减少degree%后的值
    @param buffLevel: int Buff层数
    @param num: int 生产数量
    '''
    # 计算一个单位所需时间，math.ceil返回浮点数
    unitTime = int(math.ceil(needTime / (1 + (buffLevel * degree + vipBuff) / 100.0)))

    # 设置下限值1s
    return max(unitTime * num, 1)


def discountTime_New(needTime, buffValue=0, num=1):
    '''计算needTime减少buffValue%后的值
    @param num: int 生产数量
    '''
    # 计算一个单位所需时间，math.ceil返回浮点数
    unitTime = needTime / (1 + buffValue / 100.0)

    # 设置下限值1s
    return max(int(math.ceil(unitTime * num)), 1)


def to_timestamp(dt, tz=None):
    """
    转换datetime时间为时间戳

    :param dt: datetime.datetime
    """
    if (dt.tzinfo is None) and (tz is None):
        tz = tzlocal.get_localzone().zone

    if dt.tzinfo is None:
        # 转为带时区的时间
        dt = timezone(tz).localize(dt)

    # 转为UTC
    dt = dt.replace(tzinfo=None) - dt.utcoffset()
    return (dt - datetime.datetime(1970, 1, 1)).total_seconds()


def str_to_timestamp(dt):
    """
    转换str时间(不带微秒)为时间戳
    :param dt: str
    """
    return time.mktime(time.strptime(dt, '%Y-%m-%d %H:%M:%S'))


def str_to_timestamp_ms(dt):
    """
    转换str时间(带微秒)为时间戳
    :param dt: str
    """
    d = datetime.datetime.strptime(dt, "%Y-%m-%d %H:%M:%S.%f")
    t = d.timetuple()
    time_stamp = int(time.mktime(t))
    time_stamp = float(str(time_stamp) + str("%06d" % d.microsecond)) / 1000000
    return time_stamp


def timestamp_to_str(dt):
    """
    转换str时间为时间戳
    :param dt: str
    """
    time_struct = time.localtime(dt)
    return time.strftime('%Y-%m-%d %H:%M:%S', time_struct)


def check_in_time_interval(start_time, end_time, check_time=None):
    """
    检测一天中的时段
    :param start_time: str "00:00:00"
    :param end_time: str "00:00:00"
    :param check_time: datetime
    :return: True/False
    """
    start_time = map(lambda x: int(x), start_time.split(':'))
    end_time = map(lambda x: int(x), end_time.split(':'))
    if not check_time:
        check_time = getCT()

    return check_time.replace(hour=start_time[0], minute=start_time[1], second=start_time[2]) <= check_time < \
           check_time.replace(hour=end_time[0], minute=end_time[1], second=end_time[2])


def get_some_time(dt, str_time, **kwargs):
    """
    获取指定时间
    :param dt: 指定的日期，时间格式
    :param str_time: 指定的时间（str）
    :param kwargs:
    :return:
    """
    if not checkStrIsTime(str_time):
        return None
    str_time = map(lambda x: int(x), str_time.split(':'))
    return get_another_time(dt.replace(hour=str_time[0], minute=str_time[1], second=str_time[2]), **kwargs)


def getTS():
    """获取当前时间的时间戳"""
    return time.time()


def changeTimeToMsTime(strTime):
    """将字符串时间转成微秒级"""
    try:
        datetime.datetime.strptime(strTime, "%Y-%m-%d %H:%M:%S.%f")
        return strTime
    except:
        return strTime + '.000000'


def getMonthFirstDayAndLastDay(year=None, month=None):
    """
    :param year: 年份，默认是本年，可传int或str类型
    :param month: 月份，默认是本月，可传int或str类型
    :return: firstDay: 当月的第一天，datetime.date类型
              lastDay: 当月的最后一天，datetime.date类型
    """
    if year:
        year = int(year)
    else:
        year = datetime.date.today().year

    if month:
        month = int(month)
    else:
        month = datetime.date.today().month

    # 获取当月第一天的星期和当月的总天数
    first_day_week_day, month_range = calendar.monthrange(year, month)

    # 获取当月的第一天
    first_day = datetime.datetime(year=year, month=month, day=1, hour=0, minute=0, second=0)
    last_day = datetime.datetime(year=year, month=month, day=month_range, hour=23, minute=59, second=59)

    return first_day, last_day


def format_datetime(d_time):
    """
    格式化时间
    :param d_time:
    :return:
    """
    return strToDatetime(d_time.strftime('%Y-%m-%d %H:%M:%S'))


def get_timezone():
    """
    获取当前的时区
    :return:
    """
    defaut = TimeZone.Shanghai
    return os.environ.get('TZ', defaut)


def timezone_convert(dt, from_tz_name, to_tz_name):
    """
    时区转换
    :param dt: 被转换时间
    :param from_tz: 被转换时区
    :param to_tz: 转换成时区
    :return:
    """
    from_tz = timezone(from_tz_name)
    to_tz = timezone(to_tz_name)
    
    convert = from_tz.localize(dt, is_dst=None)
    return convert.astimezone(to_tz)


def get_today_left_second(hour=23, minute=59, second=59):
    """
    获取当前今天剩余秒数
    :return:
    """
    now = getCT()
    return getCountdown(now.replace(hour=hour, minute=minute, second=second), now)


def replace_clock_time(base_time, replace_time):
    """
    替换时间（hours:minutes:seconds）
    :param base_time:
    :param replace_time:
    :return:
    """
    t = map(lambda x: int(x), replace_time.split(':'))
    return base_time.replace(hour=t[0], minute=t[1], second=t[2])


def check_daily_open_time(open_time_list):
    """
    检测开启时间格式是否正确
    :param open_time_list: ["10:00:00", "22:00:00"]
    :return:
    """
    if not isinstance(open_time_list, list):
        return False
    if len(open_time_list) != 2:
        return False
    if not checkStrIsTime(open_time_list[0]) or not checkStrIsTime(open_time_list[1]):
        return False
    if strToTime(open_time_list[0]) > strToTime(open_time_list[1]):
        return False
    return True


def get_one_day_start_time(date_time):
    """
    获取某天的起始时间并返回
    例如：2019-9-23 00:00:00
    :param date_time:
    :return:
    """
    date_time_str = date_time.strftime("%Y-%m-%d")
    date_time_str = date_time_str + " 00:00:00"
    one_day_start_time = strToDatetime(date_time_str)
    return one_day_start_time


def get_one_day_end_time(date_time):
    """
    获取某天的结束时间并返回
    例如：2019-9-23 23:59:59
    :param date_time:
    :return:
    """
    date_time_str = date_time.strftime("%Y-%m-%d")
    date_time_str = date_time_str + " 23:59:59"
    one_day_end_time = strToDatetime(date_time_str)
    return one_day_end_time


def get_yesterday():
    """
    获取当天的前一天
    :param date_time:
    :return:
    """

    yesterday = (date.today() + timedelta(days=-1))
    return yesterday


def datetime_to_timestamp(dt):
    """
    日期时间转时间戳
    :param dt: 日期时间
    :return: 时间戳
    """
    if not isinstance(dt, basestring):
        dt = '{dt_str}'.format(dt_str=dt)
    return int(time.mktime(time.strptime(dt, '%Y-%m-%d %H:%M:%S')))


def timestamp_to_datetime(timestamp, split='-'):
    """
    转换str时间为时间戳
    :param timestamp: 时间戳
    :param split: 间隔
    """
    time_struct = time.localtime(timestamp)
    str_time = time.strftime('%Y-%m-%d %H:%M:%S', time_struct)
    return datetime.datetime.strptime(str_time, "%Y" + split + "%m" + split + "%d %H:%M:%S")


def seconds_to_time_str(seconds):
    """
    秒数转换为时间字符串
    :param seconds: 秒数
    :return: 50:20:01
    """
    m, s = divmod(seconds, 60)
    h, m = divmod(m, 60)
    time_str = "{:02d}:{:02d}:{:02d}".format(h, m, s)
    return time_str


def is_same_month(dt, base_time=None):
    """
    判断某个日期是否和今天是同一月
    :param dt: datetime实例，目标时间
    :param base_time: datetime实例，基础时间
    :return:
    """
    dt_year = dt.year
    dt_month = dt.month
    if base_time is None:
        base_time = getCT()
    ct_year = base_time.year
    ct_month = base_time.month
    if (dt_year, dt_month) == (ct_year, ct_month):
        return True
    return False


def is_same_week(base_time, target_time):
    """
    是否在同一周内
    :param base_time:
    :param target_time:
    :return:
    """
    day_start_time = base_time.replace(hour=0, minute=0, second=0, microsecond=0)
    week_day = day_start_time.weekday()
    start_time = addTimes(day_start_time, days=-week_day)
    end_time = addTimes(day_start_time, days=7-week_day)
    if start_time <= target_time < end_time:
        return True
    return False


def get_hour_minute_second(ct):
    """
    获取当前时间的点的小时、分钟、秒钟
    :return:
    """
    if not ct:
        ct = getCT()
    hour, minute, second = ct.strftime("%H:%M:%S").split(":")
    return hour, minute, second


def deal_open_time(start_dt, end_dt, start_time_str, end_time_str):
    """
    从时间段内取出每天一个时段

    eg: 2021-05-01 00:00:00 2021-05-03 23:59:59 10:00:00 22:00:00
        [[2021-05-01 10:00:00 2021-05-01 22:00:00],
         [2021-05-02 10:00:00 2021-05-02 22:00:00],
         [2021-05-03 10:00:00 2021-05-03 22:00:00]]
    """

    dt_list = []

    while start_dt <= end_dt:

        cur_date_str = start_dt.strftime("%Y-%m-%d")
        start_time = datetime.datetime.strptime(
            cur_date_str + " " + start_time_str,
            "%Y-%m-%d %H:%M:%S"
        )
        end_time = datetime.datetime.strptime(
            cur_date_str + " " + end_time_str,
            "%Y-%m-%d %H:%M:%S"
        )

        if ((start_dt <= start_time <= end_dt)
                and (start_dt <= end_time <= end_dt)):
            dt_list.append([start_time, end_time])

        # 加一天
        start_dt += datetime.timedelta(days=1)

    return dt_list


def mock_time(year, month, day, hour=0, minute=0, second=0):
    """
    生成任意时间
    :return: datetime
    """
    st = (year, month, day, hour, minute, second, 0, 0, 0)
    t = time.mktime(st)
    m_time = datetime.datetime.fromtimestamp(t)
    return m_time


if __name__ == '__main__':
    seoul_now = timezone_convert(getCT(), TimeZone.Shanghai, TimeZone.Seoul)
    print format_datetime(seoul_now)
    print getCT(tz=TimeZone.Tokyo)
    print getCT(tz=TimeZone.UTC)
